home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Mania 5
/
MacMania 5.toast
/
/
Internet software
/
NewsWatcher
/
NW Source
/
Source
/
search.c
< prev
next >
Wrap
Text File
|
1997-01-09
|
9KB
|
360 lines
/*----------------------------------------------------------------------------
search.c
This module handles the "Search" command.
Copyright © 1994-1997, Northwestern University.
----------------------------------------------------------------------------*/
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "glob.h"
#include "search.h"
#include "dialog.h"
#include "newswatcher.h"
#include "mark.h"
#include "menus.h"
#include "news.h"
#include "group.h"
#include "popuputil.h"
#include "subscribe.h"
#include "status.h"
#include "memutil.h"
#include "strutil.h"
#include "header.h"
#include "full.h"
#include "biglist.h"
#define kSearchDlg 140 /* Search dialog */
#define kSearchHeader 3
#define kSearchPattern 4
#define kSearchPopup 5
/*----------------------------------------------------------------------------
DoSearchDialog
Present the search dialog.
Exit: function result = error code.
header = header name.
pattern = search string.
----------------------------------------------------------------------------*/
static OSErr DoSearchDialog (CStr255 header, CStr255 pattern)
{
static CStr255 headerSave = "";
static CStr255 patternSave = "";
DialogPtr dlg = nil;
short item;
OSErr err = noErr;
if (*headerSave == 0) GetCString(kStrDefaultSearchHdr, headerSave);
err = MyGetNewDialog(kSearchDlg, ok, cancel, &dlg);
if (err != noErr) return err;
RestoreMovableModalDialogPosition(dlg, gPrefs.searchLoc);
strcpy(header, headerSave);
strcpy(pattern, patternSave);
DlgSetCString(dlg, kSearchHeader, header);
SetItemKeyword(dlg, kSearchHeader);
SetItemMaxLength(dlg, kSearchHeader, 255);
DlgSetCString(dlg, kSearchPattern, pattern);
SetItemMaxLength(dlg, kSearchPattern, 255);
SelectDialogItemText(dlg, kSearchPattern, 0, 255);
SetItemPopupTypeinItem(dlg, kSearchPopup, kSearchHeader);
do {
DlgEnableItem(dlg, ok, *header != 0 && *pattern != 0);
MyMovableModalDialog(dlg, DialogFilter, &item);
switch (item) {
case kSearchPopup:
case kSearchHeader:
DlgGetCString(dlg, kSearchHeader, header);
break;
case kSearchPattern:
DlgGetCString(dlg, kSearchPattern, pattern);
break;
}
} while (item != ok && item != cancel);
SaveMovableModalDialogPosition(dlg, &gPrefs.searchLoc);
err = DoClose(dlg);
if (err != noErr) return err;
if (item == cancel) return userCanceledErr;
strcpy(headerSave, header);
strcpy(patternSave, pattern);
return noErr;
}
/*----------------------------------------------------------------------------
SearchOneGroup
Search a single group.
Entry: header = name of the header to be searched.
pattern = search string.
*theGroup = group record.
Exit: function result = error code.
theGroup->numUnread = number of matched articles.
theGroup->unread = handle to unread list.
----------------------------------------------------------------------------*/
static OSErr SearchOneGroup (char *header, char *pattern, TGroup *theGroup)
{
CStr255 groupName, statusStr;
THeader **headers = nil;
short numHeaders;
THeader *pHeader, *pHeaderEnd;
long firstUnread, lastUnread;
long number;
OSErr err = noErr;
Boolean groupExists;
strcpy(groupName, *gGroupNames + theGroup->nameOffset);
GetCString(kStrSearchingStatusMsg, statusStr);
strcat(statusStr, groupName);
err = DisplayStatusMessage(statusStr);
if (err != noErr) return err;
err = GetGroupArticleRange(theGroup, &groupExists);
if (err != noErr) return err;
if (!groupExists) return noErr;
theGroup->numUnread = 0;
err = SearchHeaders(groupName, header, theGroup->firstMess,
theGroup->lastMess, pattern, &headers, &numHeaders);
if (err != noErr) return err;
if (headers == nil) return noErr;
MyHLock(headers);
pHeaderEnd = *headers + numHeaders;
firstUnread = 0;
for (pHeader = *headers; pHeader < pHeaderEnd; pHeader++) {
number = pHeader->number;
if (firstUnread == 0) {
firstUnread = lastUnread = number;
} else if (pHeader->number == lastUnread+1) {
lastUnread = number;
} else {
err = AppendUnreadRange(firstUnread, lastUnread, theGroup);
if (err != noErr) goto exit;
firstUnread = lastUnread = number;
}
}
if (firstUnread != 0) {
err = AppendUnreadRange(firstUnread, lastUnread, theGroup);
if (err != noErr) goto exit;
}
MyDisposeHandle(headers);
return noErr;
exit:
MyDisposeHandle(headers);
return err;
}
/*----------------------------------------------------------------------------
SearchGroups
Search groups on the server and build a new group array containing
the matching groups and article lists.
Entry: wind = pointer to group, subject, or article window.
header = header name.
pattern = search string.
Exit: function result = error code.
*newGroupArray = array of matching group records.
*newNumGroups = number of matching group records.
----------------------------------------------------------------------------*/
static OSErr SearchGroups (WindowPtr wind, char *header, char *pattern,
TGroup ***newGroupArray, long *newNumGroups)
{
TWindow **info;
TWindowKind kind;
BigListRef groupList;
long index, item;
TGroup **groupArray;
long **groupNameOffsets;
Boolean userGroupList;
TGroup theGroup;
TGroup **theNewGroupArray = nil;
long theNewNumGroups = 0;
long numAllocated;
OSErr err = noErr;
Boolean done = false;
Boolean firstTime = true;
Handle newsgroups = nil;
long i, j, nameOffset, len;
CStr255 groupName;
char c;
info = (TWindow**)GetWRefCon(wind);
kind = (**info).kind;
err = MyNewHandle(100*sizeof(TGroup), &theNewGroupArray);
if (err != noErr) goto exit;
numAllocated = 100;
switch (kind) {
case kGroup:
userGroupList = (**info).groupKind == kUserGroup;
groupList = (**info).groupList;
groupArray = (**info).groupArray;
groupNameOffsets = (**info).groupNameOffsets;
item = 0;
break;
case kSubject:
break;
case kArticle:
err = FindHeaderHandle((**info).fullText, "Newsgroups", &newsgroups);
if (err != noErr) goto exit;
len = MyGetHandleSize(newsgroups);
i = 0;
break;
}
while (!done) {
switch (kind) {
case kGroup:
item = BigLGetNextSelectedItem(groupList, item);
done = item < 0;
if (done) break;
index = BigLGetData(groupList, item);
if (userGroupList) {
theGroup.nameOffset = (*groupArray)[index].nameOffset;
} else {
theGroup.nameOffset = (*groupNameOffsets)[index];
}
item++;
break;
case kSubject:
done = !firstTime;
if (done) break;
firstTime = false;
theGroup.nameOffset = (**info).groupNameOffset;
break;
case kArticle:
while (true) {
while (i < len && !isalnum((*newsgroups)[i])) i++;
done = i >= len;
if (done) break;
j = 0;
while (i < len && j < 256) {
c = (*newsgroups)[i];
if (isalnum(c) || c == '.') {
groupName[j++] = c;
i++;
} else {
break;
}
}
done = j >= 256;
if (done) break;
groupName[j] = 0;
nameOffset = FindGroupOffset(groupName);
if (nameOffset != -1) {
theGroup.nameOffset = nameOffset;
break;
}
}
break;
}
if (done) break;
theGroup.firstMess = theGroup.lastMess = theGroup.numUnread = 0;
theGroup.unread = nil;
err = SearchOneGroup(header, pattern, &theGroup);
if (err != noErr) goto exit;
if (theGroup.numUnread != 0) {
if (theNewNumGroups >= numAllocated) {
numAllocated += 100;
err = MySetHandleSize(theNewGroupArray, numAllocated*sizeof(TGroup));
if (err != noErr) goto exit;
}
(*theNewGroupArray)[theNewNumGroups] = theGroup;
theNewNumGroups++;
}
}
MySetHandleSize(theNewGroupArray, theNewNumGroups*sizeof(TGroup));
*newGroupArray = theNewGroupArray;
*newNumGroups = theNewNumGroups;
MyDisposeHandle(newsgroups);
return noErr;
exit:
DisposeGroupArray(theNewGroupArray, theNewNumGroups);
MyDisposeHandle(newsgroups);
return err;
}
/*----------------------------------------------------------------------------
DoSearch
Handle the "Search" command.
Entry: wind = pointer to group, subject, or article window.
Exit: function result = error code.
----------------------------------------------------------------------------*/
OSErr DoSearch (WindowPtr wind)
{
CStr255 header, pattern;
TGroup **groupArray = nil;
long numGroups;
WindowPtr newWind;
TWindow **info;
Str255 title;
OSErr err = noErr;
TSavedWindPos pos;
/* Present the search dialog. */
err = DoSearchDialog(header, pattern);
if (err != noErr) return err;
/* Search the groups. */
err = SearchGroups(wind, header, pattern, &groupArray, &numGroups);
if (err != noErr) return err;
if (numGroups == 0) {
NoteMessageNumber(kStrNoMatches);
MyDisposeHandle(groupArray);
return noErr;
}
/* Create the window. */
GetPString(kStrSearchWindowTitle, title);
pos.valid = false;
err = MakeUserGroupWindow(title, groupArray, numGroups, &pos, &newWind);
if (err != noErr) return err;
info = (TWindow**)GetWRefCon(newWind);
(**info).okToCloseIfChanged = true;
return noErr;
}